﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameLayer;
using Microsoft.Xna.Framework;
using GameMap;

namespace MainGame.GameMap
{
    public class MapDijkstra : TiledLayer
    {

        public MapDijkstra(int rows, int columns, int tileWidth, int tileHeight, params Sprite[] sprites)
            : base(rows, columns, tileWidth, tileHeight, sprites)
        {
        }

        public Point Position2RoundColumn(Vector2 pos)
        {
            Point rc = new Point();
            rc.X = (int)pos.Y / TileHeight;
            rc.Y = (int)pos.X / TileWidth;
            return rc;
        }

        public int Position2Vertext(Vector2 pos)
        {
            Point rc = Position2RoundColumn(pos);
            return RowColumn2GlobalVertex(rc.X, rc.Y);
        }

        public int RowColumn2GlobalVertex(int row, int column)
        {
            int v = column + _columns * row;
            return v;
        }

        public Point Global_Vertex2RowColumn(int vertex)
        {
            int column = vertex % _columns;
            int row = (vertex - column) / _columns;
            return new Point(row, column);
        }

        public Vector2 Global_RoundColumn2Position(Point rc)
        {
            return Global_RoundColumn2Position(rc.X, rc.Y);
        }

        public Vector2 Global_RoundColumn2Position(int r, int c)
        {
            return new Vector2(c * TileWidth, r * TileHeight);
        }


        public int LocalDijkstra_GetIndex(Point globalRoundColumn, int rowStart, int columnStart, int columnEnd)
        {
            int nRowFull = Math.Max(globalRoundColumn.X - rowStart, 0);
            return nRowFull * (columnEnd - columnStart + 1) + globalRoundColumn.Y - columnStart;
        }


        public Point LocalDijkstra_Index2RoundColumn(int localDijstra_Index, int nDijkstracolumns)
        {
            int column = localDijstra_Index % nDijkstracolumns;
            int row = (localDijstra_Index - column) / nDijkstracolumns;
            return new Point(row, column);
        }

        private bool InViewport(Point global_CurrentRC, int rowStart, int rowEnd, int colStart, int colEnd)
        {
            return global_CurrentRC.X >= rowStart && global_CurrentRC.X <= rowEnd && global_CurrentRC.Y >= colStart && global_CurrentRC.Y <= colEnd;
        }

        public Dijkstra GetDijkstra(float width, float height)
        {
            Dictionary<int, Point> mapRCIndexDijstra = new Dictionary<int, Point>();
            int rowStart = (int)Position.Y / TileHeight - 1;
            int colStart = (int)Position.X / TileWidth - 1;
            rowStart = (int)MathHelper.Clamp(rowStart - 1, 0, _rows - 1);
            colStart = (int)MathHelper.Clamp(colStart - 1, 0, _columns - 1);

            int rowEnd = (int)MathHelper.Clamp((Position.Y + height) / TileHeight + 1, 0, _rows - 1);
            int colEnd = (int)MathHelper.Clamp((Position.X + width) / TileWidth + 1, 0, _columns - 1);

            int firstVertex = RowColumn2GlobalVertex(rowStart, colStart);
            int lastVertext = RowColumn2GlobalVertex(rowEnd, colEnd);
            int n = (rowEnd - rowStart + 1) * (colEnd - colStart + 1);
            int[,] g = new int[n, n];
            for (int i = firstVertex; i <= lastVertext; i++)
            {
                Point global_CurrentRC = Global_Vertex2RowColumn(i);
                if (!InViewport(global_CurrentRC, rowStart, rowEnd, colStart, colEnd))
                    continue;
                bool lienthong = this.Tiles[global_CurrentRC.X, global_CurrentRC.Y] < 0;
                if (lienthong == false)
                    continue;
                int local_CurrentIndexD = LocalDijkstra_GetIndex(global_CurrentRC, rowStart, colStart, colEnd);
                g[local_CurrentIndexD, local_CurrentIndexD] = 0;
                mapRCIndexDijstra.Add(local_CurrentIndexD, global_CurrentRC);
                if (i - 1 >= firstVertex)
                {
                    //Đỉnh ngay phía sau
                    var global_RCLT = Global_Vertex2RowColumn(i - 1);
                    if (InViewport(global_RCLT, rowStart, rowEnd, colStart, colEnd))
                    {
                        if (this.Tiles[global_RCLT.X, global_RCLT.Y] < 0)
                        {
                            int local_IndexLTD = LocalDijkstra_GetIndex(global_RCLT, rowStart, colStart, colEnd);
                            g[local_CurrentIndexD, local_IndexLTD] = 1;
                        }
                    }
                }
                if (i + 1 <= lastVertext)
                {
                    //Đỉnh ngay trước
                    var global_RCLT = Global_Vertex2RowColumn(i + 1);
                    if (InViewport(global_RCLT, rowStart, rowEnd, colStart, colEnd))
                    {
                        if (this.Tiles[global_RCLT.X, global_RCLT.Y] < 0)
                        {
                            int local_IndexLTD = LocalDijkstra_GetIndex(global_RCLT, rowStart, colStart, colEnd);
                            g[local_CurrentIndexD, local_IndexLTD] = 1;
                        }
                    }
                }
                if (i - _columns >= firstVertex)
                {
                    //Đỉnh phía trên
                    var global_RCLT = Global_Vertex2RowColumn(i - _columns);
                    if (InViewport(global_RCLT, rowStart, rowEnd, colStart, colEnd))
                    {
                        if (this.Tiles[global_RCLT.X, global_RCLT.Y] < 0)
                        {
                            int local_IndexLTD = LocalDijkstra_GetIndex(global_RCLT, rowStart, colStart, colEnd);
                            g[local_CurrentIndexD, local_IndexLTD] = 1;
                        }
                    }
                }
                if (i + _columns <= lastVertext)
                {
                    //Đỉnh ngay dưới
                    var global_RCLT = Global_Vertex2RowColumn(i + _columns);
                    if (InViewport(global_RCLT, rowStart, rowEnd, colStart, colEnd))
                    {
                        if (this.Tiles[global_RCLT.X, global_RCLT.Y] < 0)
                        {
                            int local_IndexLTD = LocalDijkstra_GetIndex(global_RCLT, rowStart, colStart, colEnd);
                            g[local_CurrentIndexD, local_IndexLTD] = 1;
                        }
                    }
                }
            }
            var dijkstra = new Dijkstra(g, mapRCIndexDijstra);
            dijkstra.rowStart = rowStart;
            dijkstra.columnStart = colStart;
            dijkstra.columnEnd = colEnd;
            return dijkstra;
        }

        public List<Vector2> RowColumns2Positions(List<Point> rc)
        {
            List<Vector2> postions = new List<Vector2>();
            if (rc != null)
                foreach (var v in rc)
                {
                    Vector2 pos = Global_RoundColumn2Position(v);
                    postions.Add(pos);
                }
            return postions;
        }
    }
}